import * as fs from 'fs'
import { Application } from 'egg'
import { CommonData } from './common-data'
// import socketIo from './socket.io-event'
import { UnitOfWork } from './uow'

class Startup {
  static async syncDb(app: Application) {
    const { models } = app.model

    // app.logger.info(JSON.stringify(Object.keys(models)))
    // app.logger.info(JSON.stringify(Object.keys(app.model)))
    Object.keys(models).forEach(name => {
      const model = models[name]
      if (model.associate) model.associate(models)
    })

    await app.model.sync({ force: app.config.settings.dbSyncForce })
  }

  static async initUnitOfWork(app: Application) {
    app.uow = new UnitOfWork(app)
  }

  static async initPath(app: Application) {
    const { uploadPath } = app.config.settings
    return new Promise((resolve, reject) => {
      fs.exists(uploadPath, exists => {
        if (!exists) {
          fs.mkdir(uploadPath, err => {
            if (err) reject(err)
            resolve(uploadPath)
          })
        }
        resolve(uploadPath)
      })
    })
  }

  // static async initValidator(app: Application) {
  //   app.validator.addRule('json', (rule, value) => {
  //     try {
  //       JSON.parse(value)
  //       return null
  //     } catch (err) {
  //       // validator error 不需要抛出，框架内部会处理，避免服务端报错
  //       return 'must be json string'
  //     }
  //   })
  // }

  static async initService(app: Application) {
    // app.logger.info(Object.keys(app))
  }

  static async initCommonData(app: Application) {
    app.commonData = CommonData
  }

  static async createViews(app: Application, ignore: boolean) {
    if (ignore) return null

    const sqls: any[] = []
    sqls.push({
      name: 'vInventoryList',
      sql: `select wi.*, w.code "warehouseCode"
  , cu."nickName" "createdUserNickName", uu."nickName" "updatedUserNickName"
  , p.code "productCode", p.name "productName", p.country "productCountry"
  , p."plantNo" "productPlantNo", p.spec "productSpec", p.photos "productPhotos"
  , g."cnName" "goodsCnName", wil."logCount"
from "warehouseItems" wi
left join warehouses w on w.id=wi."warehouseId"
left join products p on p.id=wi."productId"
left join goods g on g.id=p."goodId"
left join users cu on cu.id=wi."createdBy"
left join users uu on uu.id=wi."updatedBy"
left join "purchaseContractItems" pci on pci.id=wi."purchaseContractItemId"
left join (select "warehouseItemId", count(*) "logCount" from "warehouseItemLogs" group by "warehouseItemId") wil
  on wil."warehouseItemId"=wi.id`,
    })

    const { username } = app.config.sequelize
    const options = {}
    for (const viewSql of sqls) {
      const sql = `
    -- drop view if exists "public"."${viewSql.name}";
    create or replace view "public"."${viewSql.name}" as ${viewSql.sql};
    -- alter table "public"."${viewSql.name}" owner to "${username}";
    `
      await app.model.query(sql, options)
        .spread((results, metadata) => {
          const metadataStr = Array.isArray(metadata) ? metadata.map(cmd => cmd.command) : metadata['command']
          app.logger.info(`create view '${viewSql.name}' metadata: ${metadataStr} results: ${results}`)
        })
    }
    return sqls
  }
}

export default async (app: Application) => {
  app.logger.info('app/core/startup.js processing...')
  await Startup.syncDb(app)
  await Startup.createViews(app, true)
  await Startup.initUnitOfWork(app)
  await Startup.initPath(app)
  // await Startup.initValidator(app)
  await Startup.initService(app)
  await Startup.initCommonData(app)
  // await socketIo(app)
  app.logger.info('app/core/startup.js done')
}
